/*
 *
 *  Copyright (C) 2010 Tal Eisenberg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package base;

import tal.drivechain.plugin.Module;
import tal.drivechain.variable.Variable;
import tal.drivechain.matchers.VarMatcher;
import tal.drivechain.plugin.Channel;

/**
 *
 * @author Tal Eisenberg
 */
public class Clock extends Module {
    private VarMatcher ontrigger = null;
    private VarMatcher offtrigger = null;
    private boolean running = false;
    private Variable out = null;
    private long freq;
    private ClockThread rThread;
    
    public void setOutput(Object value) {
        out = (Variable)value;
    } public Object getOutput() { return out; }
    
    public void setOnTrigger(Object value) {
        ontrigger = (VarMatcher)value;
    } public Object getOnTrigger() { return ontrigger; }
    
    public void setOffTrigger(Object value) {
        offtrigger = (VarMatcher)value;
    } public Object getOffTrigger() { return offtrigger; }
    
    public void setFreq(Long value) {
        freq = (Long)value;
    } public Object getFreq() { return freq; }
    
    @Override
    protected void msgReceived(Variable in, long timestamp, Channel channel) {
        if (ontrigger.matches(in, channel, this) && !running) {
            if (infoFlag) log("Starting clock...", MSG.INFO);
            startClock();
        } else if (offtrigger.matches(in, channel, this) && running) {
            if (infoFlag) log("Stopping clock.", MSG.INFO);
            stopClock();
        }
    }
    
    public long getCurrentFreq() {
        return freq;
    }

    private void send() {
        sendToOutputs(out, -1);
    }

    private void startClock() {
        if (running) return;
        running = true;
        rThread = new ClockThread(this);
        rThread.start();
    }

    private void stopClock() {
        if (!running) return;
        running = false;
        rThread.interrupt();
        rThread = null;
    }

    @Override
    public void stop() {
        stopClock();
        super.stop();
    }

    class ClockThread extends Thread {
        Clock r;

        public ClockThread(Clock repeater) {
            r = repeater;
        }
        
        @Override
        public void run() {
            while (true) {
                long beginNano = System.nanoTime();
                r.send(); // this is where any time consumed by modules connected to this Clock is used.
                long deltaNano = System.nanoTime() - beginNano;
                long durNano = r.getCurrentFreq()*1000000 - deltaNano;
                long durMilli = (long)durNano/1000000;
                int durNanos = (int)(durNano - durMilli*1000000);
                if (durNanos<0) durNanos = 0;
                if (durMilli<0) durMilli = 0;
                if (freq!=0) {
                    synchronized(this) {
                        try {
                            if (durMilli>0 || durNanos>0)
                                this.wait(durMilli, durNanos);
                        } catch (InterruptedException ie) { }
                    }
                } else return;

            }
        }
    }
    
}